package cc.rengu.igas.route.core.model;

import cc.rengu.igas.route.common.constant.SmartRouteParamConstant;
import cc.rengu.igas.route.common.entity.MultiDyncDataStatic;
import cc.rengu.utility.base.StringUtil;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 多路比例动态数据按照规则分组存储，一个规则中有多条
 *
 * @author lizhangtao
 * @version 1.0
 * @date 20200722
 */
public class MultiDyncDataRuleGroup {
    public ArrayList<MultiDyncDataStatic> getMultiDyncDataRuleArrayList() {
        return multiDyncDataRuleArrayList;
    }

    /**
     * 多路比例统计的动态数据，已经使用其配额从小到大排序
     */
    private ArrayList<MultiDyncDataStatic> multiDyncDataRuleArrayList;
    /**
     * 表示本次需要获取的多路比例数据的位置
     */
    private AtomicInteger nowIndex = new AtomicInteger(0);
    private int nowIndexMax;
    /**
     * 表示使用量已满的通道的位置，初始位置为-1
     */
    private AtomicInteger fullIndex = new AtomicInteger(-1);
    private boolean isAllInvalid = false;
    /**
     * 保证原子操作
     */
    private ReentrantLock lock = new ReentrantLock();

    public MultiDyncDataRuleGroup(ArrayList<MultiDyncDataStatic> dataStatics) {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        multiDyncDataRuleArrayList = dataStatics;
        nowIndexMax = dataStatics.size() - 1;
        int i = 0;
        for (MultiDyncDataStatic multiDyncDataStaticTmp : multiDyncDataRuleArrayList) {
            //遇到一个状态正常的即退出循环
            if (SmartRouteParamConstant.YES.equalsIgnoreCase(multiDyncDataStaticTmp.getChannelStatus()) &&
                    SmartRouteParamConstant.YES.equalsIgnoreCase(multiDyncDataStaticTmp.getChannelRealStatus())) {
                break;
            } else {
                i++;
            }
        }
        if (i >= nowIndexMax) {
            rglog.error("多路比例通道全部不可用");
            isAllInvalid = true;
        }
    }

    /**
     * 获取当前选择的通道
     * 1. fullIndex最小值-1，最大值ArrayList.size-2；
     * 2. nowIndex最小值0，最大值ArrayList.size-1；
     * 3. 如果所有的通道都不可用直接返回null
     * 3. 取当前通道并将位置nowIndex++,
     * 4. 判断当前通道为最后一个通道且此次做完之后没有满额->设置下一次的取通道的位置为fullIndex+1
     * 5. 判断当前通道为最后一个通道且此次做完之后已经满额->重新开始，所有计数器全部重置
     * 6. 判断当前通道不为最后一个通道且此次做完之后没有满额->不处理
     * 7. 判断当前通道不为最后一个通道且此次做完之后已经满额->fullIndex++
     * 8. 若当前通道状态异常则递归调用
     *
     * @return 总是返回当前选择的通道的动态统计数据
     * @throws Exception
     */
    public MultiDyncDataStatic getNowDstChannelDyncData() throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        lock.lock();
        try {
            if (null == multiDyncDataRuleArrayList || nowIndexMax < 1) {
                rglog.error("多路比例通道为空或未配置");
                return null;
            }
            //如果所有的通道都不可用直接返回null，用于递归的退出，只要有一个通道正常都会退出
            if (isAllInvalid) {
                rglog.error("多路比例通道全部不可用");
                return null;
            }

            //取当前通道并将下一次需要取的位置+1
            int now = nowIndex.getAndIncrement();
            MultiDyncDataStatic multiDyncDataStatic = multiDyncDataRuleArrayList.get(now);
            multiDyncDataStatic.setWeightUsed(multiDyncDataStatic.getWeightUsed() + 1);
            //判断当前通道为最后一个通道且此次做完之后该通道没有满额->设置下一次的取通道的位置为fullIndex+1
            if (now >= nowIndexMax && multiDyncDataStatic.getWeight() > multiDyncDataStatic.getWeightUsed()) {
                rglog.debug("当前通道为最后一个通道<index:{},name:{},used:{}>且此次做完之后该通道没有满额<{}>->设置下一次的取通道的位置为fullIndex+1",
                        now, multiDyncDataStatic.getChannelId(), multiDyncDataStatic.getWeightUsed(), multiDyncDataStatic.getWeight());
                nowIndex.set(fullIndex.get() + 1);
            }
            //判断当前通道为最后一个通道且此次做完之后该通道已经满额->重新开始，所有计数器全部重置
            else if (now >= nowIndexMax && multiDyncDataStatic.getWeight() <= multiDyncDataStatic.getWeightUsed()) {
                rglog.debug("当前通道为最后一个通道<index:{},name:{},used:{}>且此次做完之后该通道已经满额<{}>->重新开始，所有计数器全部重置",
                        now, multiDyncDataStatic.getChannelId(), multiDyncDataStatic.getWeightUsed(), multiDyncDataStatic.getWeight());
                this.nowIndex.set(0);
                this.fullIndex.set(-1);
                for (MultiDyncDataStatic multiDyncDataStaticTmp : multiDyncDataRuleArrayList) {
                    multiDyncDataStaticTmp.setWeightUsed(0);
                }
            }
            //判断当前通道不为最后一个通道且此次做完之后该通道没有满额->不处理
            else if (now < nowIndexMax && multiDyncDataStatic.getWeight() > multiDyncDataStatic.getWeightUsed()) {
                rglog.debug("当前通道不为最后一个通道<index:{},name:{},used:{}>且此次做完之后该通道没有满额<{}>->不处理",
                        now, multiDyncDataStatic.getChannelId(), multiDyncDataStatic.getWeightUsed(), multiDyncDataStatic.getWeight());
            }
            //判断当前通道不为最后一个通道且此次做完之后该通道已经满额->fullIndex++
            else {
                rglog.debug("当前通道不为最后一个通道<index:{},name:{},used:{}>且此次做完之后该通道已经满额<{}>->fullIndex++",
                        now, multiDyncDataStatic.getChannelId(), multiDyncDataStatic.getWeightUsed(), multiDyncDataStatic.getWeight());
                fullIndex.incrementAndGet();
            }
            //该通道正常直接返回
            if (SmartRouteParamConstant.YES.equalsIgnoreCase(multiDyncDataStatic.getChannelStatus()) &&
                    SmartRouteParamConstant.YES.equalsIgnoreCase(multiDyncDataStatic.getChannelRealStatus())) {
                return multiDyncDataStatic;
            }
            //该通道状态异常递归
            else {
                return getNowDstChannelDyncData();
            }
        } catch (Exception e) {
            rglog.error(StringUtil.ExceptionToString(e));
            throw e;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 选择计数器及已满计数器位置设置
     * 通过当前的数据直接设置
     */
    public void setNowAndFullIndex() throws Exception {
        lock.lock();
        try {
            if (multiDyncDataRuleArrayList.size() < SmartRouteParamConstant.INT_TWO) {
                throw new Exception("多路比例动态统计数据错误，至少需要2条数据");
            }
            //当前使用量小于配额
            ArrayList<MultiDyncDataStatic> arrayListTmp = new ArrayList<>();
            for (MultiDyncDataStatic multiDyncDataStatic : multiDyncDataRuleArrayList) {
                if (multiDyncDataStatic.getWeight() > multiDyncDataStatic.getWeightUsed()) {
                    arrayListTmp.add(multiDyncDataStatic);
                }
            }
            //全部满额
            if (arrayListTmp.isEmpty()) {
                this.nowIndex.set(0);
                this.fullIndex.set(-1);
                for (MultiDyncDataStatic multiDyncDataStaticTmp : multiDyncDataRuleArrayList) {
                    multiDyncDataStaticTmp.setWeightUsed(0);
                }
            } else {
                MultiDyncDataStatic multiDyncDataStaticMin = Collections.min(arrayListTmp, Comparator.comparingInt(MultiDyncDataStatic::getWeightUsed));
                int nowIndexInt = multiDyncDataRuleArrayList.indexOf(multiDyncDataStaticMin);
                int fullIndexInt = multiDyncDataRuleArrayList.indexOf(arrayListTmp.get(0)) - 1;
                //若当前通道到达最后一个通道且使用量已满则翻转当前位置，并重置所有计数器
                if (nowIndexInt >= nowIndexMax && multiDyncDataStaticMin.getWeightUsed() >= multiDyncDataStaticMin.getWeight()) {
                    this.nowIndex.set(0);
                    this.fullIndex.set(-1);
                    for (MultiDyncDataStatic multiDyncDataStaticTmp : multiDyncDataRuleArrayList) {
                        multiDyncDataStaticTmp.setWeightUsed(0);
                    }
                } else {
                    this.nowIndex.set(nowIndexInt);
                    this.fullIndex.set(fullIndexInt);
                }
            }
        } catch (Exception e) {

            throw e;
        } finally {
            lock.unlock();
        }
    }
}
